# coding=utf-8
import json
from datetime import datetime
from flask import current_app, jsonify, request, g, session
from . import api
from aihome import redis_store, constants, db
from aihome.utils.response_code import RET
from aihome.models import Area, House, Facility, HouseImage, User, Order
from aihome.utils.commons import login_required
from aihome.utils.image_storage import storage
from aihome.constants import QINIU_DOMIN_PREFIX


@api.route('/areas', methods=['GET'])
def get_areas_info():
    """
    查询城区信息
    1.查询redis缓存中是否存在
    2.从mysql中查询结果
    3.判断查询结果
    4.存放到容器中，转为json数据格式，
    5.写入到缓存当中
    6.返回结果
    :return:
    """
    try:
        areas = redis_store.get('area_info')
    except Exception as e:
        current_app.logger.error(e)
        areas = None
    if areas:
        # 留下访问redis数据库的记录
        current_app.logger.info('hit redis areas info')
        return '{"errno":0, "errmsg":"OK", "data":%s}' % areas

    # 查询数据库
    try:
        areas = Area.query.all()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(erron=RET.DATAERR, errmsg='查询城区信息异常')

    # 判断areas是否存在
    if not areas:
        return jsonify(erron=RET.DATAERR, errmsg='没有查询到城区信息')
    # 遍历areas
    areas_list = []
    for area in areas:
        # 将area对象转换为键值对，存储到列表
        areas_list.append(area.to_dict())
    # 转化为json格式
    areas_json = json.dumps(areas_list)
    # 设置缓存
    try:
        redis_store.setex('area_info', constants.AREA_INFO_REDIS_EXPIRES, areas_json)
    except Exception as e:
        current_app.logger.error(e)
    # 返回结果，城区信息已经是json字符串,不需要使用jsonify
    return '{"errno":0, "errmsg":"ok", "data":%s}' % areas_json


@api.route('/houses', methods=['POST'])
@login_required
def save_house_info():
    """
    发布新房源
    1.确认用户身份
    2.获取json数据
    3.获取房屋参数
    4.校验参数
    5.准备保存数据
    6.查询配套设施
    7.判断是否存在
    8.写入到两张表
    9.返回结果
    :return:
    """
    # 获取用户身份
    user_id = g.user_id
    # 获取json数据
    house_data = request.get_json()
    if not house_data:
        return jsonify(erron=RET.DATAERR, errmsg='没有json数据')
    # 获取详细的房屋参数信息(基本信息,除配套设施外)
    title = house_data.get('title') # 房屋标题
    area_id = house_data.get('area_id') # 房屋城区
    address = house_data.get('address') # 详细地址
    price = house_data.get('price') # 房屋价格
    room_count = house_data.get('room_count') # 房屋数目
    acreage = house_data.get('acreage') # 房屋面积
    unit = house_data.get('unit') # 房屋户型
    capacity = house_data.get('capacity') # 人数上限
    beds = house_data.get('beds') # 卧床配置
    deposit = house_data.get('deposit') # 押金
    min_days = house_data.get('min_days') # 最小入住天数
    max_days = house_data.get('max_days') # 最大入住天数
    # 检查参数的完整性
    if not all([title,area_id,address,price,room_count,acreage,unit,capacity,beds,deposit,min_days,max_days]):
        return jsonify(errno=RET.PARAMERR,errmsg='参数缺失')
    # 对价格参数进行转换,由元转成分
    try:
        price = int(float(price)*100)
        deposit = int(float(deposit)*100)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR,errmsg='价格数据错误')
    # 构造模型类对象,准备存储数据
    house = House()
    house.user_id = user_id
    house.area_id = area_id
    house.title = title
    house.address = address
    house.price = price
    house.room_count = room_count
    house.acreage = acreage
    house.unit = unit
    house.capacity = capacity
    house.beds = beds
    house.deposit = deposit
    house.min_days = min_days
    house.max_days = max_days
    # 获取房屋配套设施
    facility = house_data.get('facility')
    # 判断房屋设施存在
    if facility:
        try:
            # 过滤房屋设置编号，确保编号存在
            facilities = Facility.query.filter(Facility.id.in_(facility)).all()
            house.facilities = facilities
        except Exception as e:
            return jsonify(errno=RET.DATAERR,errmsg='房屋设施编号有误')
    # 保存数据到数据库
    try:
        db.session.add(house)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        # 发生异常回滚
        db.session.rollback()
        return jsonify(errno=RET.DATAERR, errmsg='保存房屋数据异常')
    # 返回结果,house.id是用来后面实现上传房屋图片做准备
    return jsonify(errno=RET.OK, errmsg='OK', data={'house_id': house.id})


@api.route('/houses/<int:house_id>/images',methods=['POST'])
@login_required
def save_house_image(house_id):
    """
    1.判断房屋id是否存在
    1.获取图片
    2.调用七牛云保存图片
    4.判断默认图片是否存在
    3.在数据库保存图片相对路径
    6.更新房屋信息
    3.拼接图片ｕｒｌ
    :param house_id:
    :return:
    """
    # 判断房屋数据是否存在
    try:
        house = House.query.filter_by(id=house_id).first()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='房屋数据不存在')
    # 获取图片
    image = request.files.get('house_image')
    print (image)
    if not image:
        return jsonify(errno=RET.DATAERR, errmsg='图片不存在')
    # 调用七牛云保存图片
    try:
        image_url = storage(image.read())
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='七牛云保存图片失败')

    # 创建图片对象
    house_image = HouseImage()
    house_image.house_id = house_id
    house_image.url = image_url
    # 添加图片到数据库
    try:
        db.session.add(house_image)
        db.session.commit()
    except Exception as e:
        current_app.logger.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DATAERR, errmsg='图片保存到数据库异常')
    # 判断是否有默认图片
    if not house.index_image_url:
        # house.index_image_url = image_url
        # 保存默认图片
        try:
            House.query.filter_by(id=house_id).update({'index_image_url':image_url})
            db.session.commit()
        except Exception as e:
            current_app.logger.error(e)
            db.session.rollback()
            return jsonify(errno=RET.DATAERR, errmsg='更新房屋默认图片到数据库异常')
    # 拼接图片
    url = QINIU_DOMIN_PREFIX + image_url
    # print url
    return jsonify(errno=RET.OK, errmsg='OK', data={'url':url})


@api.route('/user/houses', methods=['GET'])
@login_required
def get_user_houses():
    """
    我的房源
    1.判断用户id,确认用户身份
    2.获取用户房源
    3.定义容器以键值对形式存储房源
    5.返回数据
    :return:
    """
    # 获取用户id
    user_id = g.user_id
    # 判断用户是否存在
    try:
        user = User.query.filter_by(id=user_id).first()
    except Exception as e:
        return jsonify(errno=RET.DATAERR, errmsg='用户不存在')
    # 获取用户房源
    houses = user.houses
    houses_list = []
    if houses:
        for house in houses:
            # 将house对象转化为键值对字典存储到列表
            houses_list.append(house.to_basic_dict())
    # # 返回结果
    return jsonify(errno=RET.OK, errmsg='OK', data={'houses': houses_list})


@api.route('/houses/index', methods=['GET'])
def get_houses_index():
    """
    首页幻灯片图片展示
    1.从redis缓存中读取数据
    2.判断redis是否存在
    3.从mysql数库中读取数据
    4.根据房屋销量排序
    5.遍历房屋列表，判断房屋是否存在图片，
    6.转化为键值对形式的字典数据
    7.转化为json数据
    8.设置缓存
    9.返回给前端数据
    :return:
    """
    # 从redis缓存中读取数据
    try:
        ret = redis_store.get('home_page_data')
    except Exception as e:
        ret = None
    # 如果有缓存
    if ret:
        current_app.logger.info('hit redis houses index info')
        # 将缓存数据返回给前端
        return '{"errno":0, "errmsg":"OK", "data":%s}' % ret
    # 没有缓存从数据库中读取房屋数据
    try:
        # 根据房屋销量排序，取出前５个展示
        houses = House.query.order_by(House.order_count.desc()).limit(constants.HOME_PAGE_MAX_HOUSES)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='查询mysql数据库异常')
    #  如果没有从数据库查询到数据
    if not houses:
        return jsonify(errno=RET.DATAERR, errmsg='没有房屋数据')
    # 定义列表
    houses_list = []
    # 遍历houses获取房屋对象
    for house in houses:
        # print house
        # 如果房屋没有图片，不添加
        if not house.index_image_url:
            # 跳出当次循环
            continue
        # 转化成键值对后，添加到列表
        houses_list.append(house.to_basic_dict())
    # 转化成json格式
    houses_json = json.dumps(houses_list)
    # 缓存到redis中
    try:
        redis_store.setex('home_page_data',constants.HOME_PAGE_DATA_REDIS_EXPIRES, houses_json)
    except Exception as e:
        # 记录redis异常日志
        current_app.logger.error(e)
    # 构造json数据
    resp = '{"errno":0, "errmsg":"OK", "data":%s}' % houses_json
    # 返回给前端
    return resp


@api.route('/houses/<int:house_id>',methods=['GET'])
def get_house_detail(house_id):
    """
    房屋详情
    1.判断房屋id是否存在
    2.读取session,查看用户身份信息
    3.读取redis缓存，查询房屋详情
    4.查询数据库，获取房屋对象
    5.将房屋对象转化为键值对字典
    6.转化为json数据
    7.设置redis缓存
    8.返回数据：房屋详情数据，用户id
    :param house_id:
    :return:
    """
    # 使用session对象获取用户身份
    user_id = session.get('user_id',-1)
    # 判断house_id存在
    if not house_id:
        return jsonify(errno=RET.PARAMERR,errmsg='参数错误')
    # 尝试读取redis数据库,获取房屋详情数据
    try:
        ret = redis_store.get('house_info_%s' % house_id)
    except Exception as e:
        current_app.logger.error(e)
        ret = None
    # 判断获取结果
    if ret:
        # 留下访问redis的记录
        current_app.logger.info('hit redis house detail info')
        return '{"errno":0,"errmsg":"OK","data":{"user_id":%s,"house":%s}}' % (user_id,ret)
    # 查询磁盘数据库
    try:
        house = House.query.get(house_id)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询房屋数据失败')
    # 判断查询结果
    if not house:
        return jsonify(errno=RET.NODATA,errmsg='无房屋数据')
    # 调用模型类中的to_ful_dict()方法,因为该方法中查询了数据库,所以需要异常处理
    try:
        house_data = house.to_full_dict()
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR,errmsg='查询房屋详情数据失败')
    # 转成json
    house_json = json.dumps(house_data)
    # 把房屋详情数据存入redis缓存中
    try:
        redis_store.setex('house_info_%s' % house_id,constants.HOME_PAGE_DATA_REDIS_EXPIRES,house_json)
    except Exception as e:
        current_app.logger.error(e)
    # 构造响应报文,返回结果
    resp = '{"errno":0,"errmsg":"OK","data":{"user_id":%s,"house":%s}}' % (user_id,house_json)
    return resp


@api.route("/houses", methods=['GET'])
def get_houses_list():
    """
获取房屋列表信息
    缓存----磁盘----缓存
    获取参数---检查参数---查询数据---返回结果
    1/获取参数:aid,sd,ed,sk,p
    2/需要对排序条件和页面两个参数,进行默认处理
    3/需要对日期参数进行判断,并且进行格式化
    4/需要对页数进行格式化
    5/尝试从redis缓存中获取房屋列表信息
    6/让一个键对应多条数据的存储,需要hash数据类型,构造hash对象的键
    redis_key = 'houses_%s_%s_%s_%s' %(aid,sd,ed,sk)
    ret = redis_store.hget(redis_key,page)
    7/如果有数据,留下访问的记录,直接返回
    8/查询mysql数据库
    9/首先定义容器存储查询数据库的过滤条件
    10/判断区域参数的存在,如果有把区域信息添加到列表中
    11/需要判断日期参数的存在,把用户选择的日期和订单表中的日期进行比较,找到日期不冲突的房屋返回给客户端
    12/根据容器中存储的过滤条件,执行查询排序,price/crate_time
    houses = House.query.filter(过滤条件).order_by(House.order_count.desc())
    13/对排序结果进行分页
    houses_page = houses.paginate(page,每页条目数,False)
    total_page = houses_page.pages
    houses_list = houses_page.items
    14/定义容器,houses_dict_list遍历分页后的房屋数据,调用模型类中的to_basic_dict()方法
    15/构造响应报文
    resp = {"errno":0,"errmsg":"OK","data":{"houses":houses_dict_list,"total_page":total_page,"current_page":page}}
    16/序列化数据,准备存入缓存中
    resp_json = json.dumps(resp)
    17/判断用户选择的页数必须小于等于分页后的总页数,本质上用户选择的页数必须要有数据
    18/构造hash数据类型的键,为了确保数据的完整性以及有效期的一致性
    需要使用事务
    pip = redis_store.pipline()
    pip.multi()
    pip.hset(redis_key,page,resp_json)
    pip.expire(redis_key,7200)
    pip.execute()
    19/返回结果,return resp_json
    :return:
    """
    # 获取参数
    area_id = request.args.get('aid', '')
    start_date_str = request.args.get('sd', '')
    end_date_str = request.args.get('ed', '')
    sort_key = request.args.get('sk', 'new')
    page = request.args.get('p', '1')
    try:
        # 定义变量存储格式化后的日期
        start_date, end_date = None, None
        # 对日期字符串进行格式化输出
        if start_date_str:
            start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
        if end_date_str:
            end_date = datetime.strptime(end_date_str, '%Y-%m-%d')
        if start_date_str and end_date_str:
            # 断言开始日期小于等于结束日期
            assert start_date <= end_date
    except Exception as e:
        # 记录错误日志信息
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='日期格式错误')
    # 对页数进行格式化
    try:
        page = int(page)
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='页数格式错误')
    # 尝试从redis缓存中获取房屋列表信息
    # 一个键对应多条数据,使用hash数据类型
    try:
        redis_key = 'houses_%s_%s_%s_%s' % (area_id,start_date_str,end_date_str,sort_key)
        ret = redis_store.hget(redis_key,page)
    except Exception as e:
        current_app.logger.error(e)
        ret = None
    # 判断ret是否存在
    if ret:
        # 留下访问redis数据的记录
        current_app.logger.info('hit redis houses list info')
        return ret
    # 查询mysql数据库
    try:
        # 定义容器,存储过滤条件
        params_filter = []
        # 如果城区参数存在
        if area_id:
            # 添加的是城区数据的对象
            params_filter.append(House.area_id == area_id)
            print params_filter
        orders = []
        # 如果开始日期和结束日期都存在
        if start_date and end_date:
            # 查询日期有冲突的订单
            orders = Order.query.filter(Order.begin_date<=start_date, Order.end_date>=end_date).all()
        # 如果只有开始日期
        elif start_date:
            # 获取与日期有冲突的订单
            orders = Order.query.filter(Order.end_date>=start_date).all()
        # 如果只有结束日期
        elif end_date:
            # 获取与日期有冲突的订单
            orders = Order.query.filter(Order.begin_date<=end_date).all()

        # 遍历有冲突的房屋
        conflict_houses_id = [order.house_id for order in orders]
        if conflict_houses_id:
            # 添加不冲突的房屋id
            params_filter.append(House.id.notin_(conflict_houses_id))
        # 按房屋成交次数排序
        if 'booking' == sort_key:
            houses = House.query.filter(*params_filter).order_by(House.order_count.desc())
        # 按房屋成交价格升序排序
        elif 'price-inc' == sort_key:
            houses = House.query.filter(*params_filter).order_by(House.price.asc())
        # 按房屋成交价格降序排序
        elif 'price-des' == sort_key:
            houses = House.query.filter(*params_filter).order_by(House.price.desc())
        # 默认按时间降序排序
        else:
            houses = House.query.filter(*params_filter).order_by(House.create_time.desc())
        # 对数据进行分页
        houses_page = houses.paginate(page, constants.HOUSE_LIST_PAGE_CAPACITY, False)
        # houses_page = houses.paginate(page, constants.HOUSE_LIST_PAGE_CAPACITY, False)
        # 获取分页后的房屋数据
        houses_list = houses_page.items
        # 获取分页后的总页数
        total_page = houses_page.pages
        # 定义容器存储房屋对象
        houses_dict_list = []
        for house in houses_list:
            # 将房屋对象转为字典格式的字符串添加到列表
            houses_dict_list.append(house.to_basic_dict())
    except Exception as e:
        current_app.logger.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询房屋列表信息失败')

    # 构造响应数据
    resp = {"errno": 0, "errmsg": "OK", "data": {"houses": houses_dict_list, "total_page": total_page, "current_page": page}}
    # 转化为json数据
    resp_json = json.dumps(resp)
    # 判断用户请求的页数必须小于等于分页后的总页数
    if page <= total_page:
        redis_key = 'houses_%s_%s_%s_%s' % (area_id, start_date_str, end_date_str, sort_key)
        # 多条数据的存储,为了确保数据的完整性和一致性,需要使用事务
        pip = redis_store.pipeline()
        try:
            # 开启事务
            pip.multi()
            # 保存数据
            pip.hset(redis_key, page, resp_json)
            # 设置过期时间
            pip.expire(redis_key,constants.HOUSE_LIST_REDIS_EXPIRES)
            # 执行事务
            pip.execute()
        except Exception as e:
            current_app.logger.error(e)
    # 返回结果
    return resp_json